package com.changgou.controller;

import com.alibaba.fastjson.JSON;
import com.changgou.entity.Result;
import com.changgou.entity.StatusCode;
import com.changgou.service.WeixinPayService;
import com.github.wxpay.sdk.WXPayUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by zhangyuhong
 * Date:2020/5/20
 */
@Api("我微信支付接口")
@RestController
@RequestMapping(value = "/weixin/pay")
@CrossOrigin
public class WeixinPayController {

    @Autowired
    private WeixinPayService weixinPayService;

    /**
     * 需要传的数据
     * username:用户名,可以根据用户名查询用户排队信息
     * outtradeno：商户订单号，下单必须
     * money：支付金额，支付必须
     * from：队列名字，回调的时候，可以知道将支付信息发送到哪个队列
     *
     * @return 二维码的收款地址 ,订单id, 金额
     */
    @ApiOperation("获取二维码地址")
    @RequestMapping("/create/native")  //参数里面要包含  用户名 from 付款金额  订单id
    public Result createNative(
            @ApiParam(name = "包含订单编号,和支付金额") @RequestParam Map<String, String> parameter) {
        Map<String, String> resultMap = weixinPayService.createNative(parameter);
        return new Result(true, StatusCode.OK, "生成二维码成功", resultMap);
    }

    /***
     * 查询支付状态--订单不明的情况"主动"查询   前端会设一个定时任务,不然说5秒后  请求这个支付的接口,ok就跳转,  优化方案:使用springmvc websocket定向向父接收端发
     *
     * 这个是为了接收不到支付的查询结果主动查
     * @param out_trade_no
     * @return
     */
    @GetMapping(value = "/status/query")
    public Result queryStatus(String out_trade_no) {
        Map<String, String> resultMap = weixinPayService.queryPayStatus(out_trade_no);
        if (resultMap != null) {
            return new Result(true, StatusCode.OK, "查询状态成功！", resultMap);
        }
        return new Result(true, StatusCode.OK, "查询状态失败！");
    }


    @Value("${mq.pay.exchange.order}")
    private String exchange;
    @Value("${mq.pay.queue.order}")
    private String queue;
    @Value("${mq.pay.routing.key}")
    private String routing;

    @Autowired
    private Environment environment;


    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 微信支付状态回调  支付结果及用户信息通过数据流的形式传到这  然后我们根据这些结果 --修改订单状态--发短信给用户提示下单成功--让发货系统发货--发优惠券等(通过前面的查看订单状态也可以,不过太多了,麻烦)
     * <p>
     * 该链接是通过【统一下单API】中提交的参数notify_url设置，如果链接无法访问，商户将无法接收到微信通知。也就是
     * 通知url必须为直接可访问的url，不能携带参数。
     * <p>
     * 这个会响应给微信的支付服务器,让他不再继续回调给我,并且发一个消息给MQ  由各个其他的微服务(优惠券,短信...)  进行监控  然后慢慢处理业务  实现了异步
     *
     * @param request
     * @return
     */
    @RequestMapping(value = "/notify/url")  //因为是数据流,一般是post,这里最好不指定类型,地址:跟在application.yml配置的一样 notifyurl:的值一样
    public String notifyUrl(HttpServletRequest request) {
        InputStream inputStream = null;
        ByteArrayOutputStream outputStream = null;
        try {
            inputStream = request.getInputStream();
            outputStream = new ByteArrayOutputStream();
            int len;
            byte[] buff = new byte[1024];
            while ((len = inputStream.read(buff)) != -1) {
                outputStream.write(buff, 0, len);
            }
            // 将支付回调数据转换成xml字符串
            String content = new String(outputStream.toByteArray(), "utf-8");
            System.out.println("支付后响应的内容:" + content);
            //将xml字符串转换成Map结构
            Map<String, String> map = WXPayUtil.xmlToMap(content);
            System.out.println(map);
            //将消息发送给RabbitMQ  //(交换机名称，路由key,消息内容-->有路由名称可以使用 发布订阅模式等,让消息转发到符合routing key的队列)
            //rabbitTemplate.convertAndSend(exchange,routing, JSON.toJSONString(xmlToMap));

            String attach = map.get("attach");//jONS格式的字符串
            Map<String, String> attachMap = JSON.parseObject(attach, Map.class);
            //如果是秒杀的支付  发送消息到queu2 如果是普通的支付 发送消息到queue1
            String from = attachMap.get("from");
            switch (from) {
                case "1":
                    //普通的支付
                    rabbitTemplate.convertAndSend(exchange, routing, JSON.toJSONString(map));
                    break;
                case "2":
                    System.out.println("哈哈哈哈：=================秒杀用户支付===成功");
                    //发送消息到 秒杀订单的队列中
                    rabbitTemplate.convertAndSend(
                            environment.getProperty("mq.pay.exchange.seckillorder"),
                            environment.getProperty("mq.pay.routing.seckillkey"),
                            JSON.toJSONString(map));
                    break;
            }

            //响应数据设置  这个数据是响应回去给 微信支付别人的服务器的
            Map respMap = new HashMap();
            respMap.put("return_code", "SUCCESS");
            respMap.put("return_msg", "OK");
            return WXPayUtil.mapToXml(respMap);

        } catch (Exception e) {
            e.printStackTrace();
            //记录错误日志
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        //前面这样是可能存在问题的
        //用户支付后，有可能畅购服务网络不通或者服务器挂了，此时会导致回调地址无法接收到用户支付状态，
        // 这时候我们需要取微信服务器查询。所以我们之前订单信息的ID存入到了Redis队列，主要用于解决这种网络不可达造成支付状态无法回调获取的问题。
        //实现思路如下：
        //1.每次下单，都将订单存入到Reids List队列中
        //2.定时每5秒检查一次Redis 队列中是否有数据，如果有，则再去查询微信服务器支付状态
        //3.如果已支付，则修改订单状态(订单的状态包括订单的ID 和订单的创建时间)
        //4.如果没有支付，是等待支付，则再将订单存入到Redis队列中，等会再次检查
        //5.如果是支付失败，直接删除订单信息并修改订单状态
        return null;
    }

    /***
     * 关闭支付
     * @param orderId
     * @return
     */
    @ApiOperation("关闭支付")
    @RequestMapping("/pay/close")
    public Result closePay(Long orderId) {
        try {
            weixinPayService.closePay(orderId);
        } catch (Exception e) {
            e.printStackTrace();
            return new Result(true, StatusCode.ERROR, "出错了--不能关闭当前订单");
        }
        return new Result(true, StatusCode.OK, "关闭订单成功");
    }
}
